package {{configPackage}};

{{#authMethods}}
{{#isBasicBasic}}
import feign.auth.BasicAuthRequestInterceptor;
{{^hasApiKeyMethods}}
import org.springframework.beans.factory.annotation.Value;
{{/hasApiKeyMethods}}
{{/isBasicBasic}}
{{/authMethods}}
{{#hasAuthMethods}}
import org.springframework.context.annotation.Bean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
{{#hasApiKeyMethods}}
import org.springframework.beans.factory.annotation.Value;
{{/hasApiKeyMethods}}
{{#hasOAuthMethods}}
import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.oauth2.client.AuthorizedClientServiceOAuth2AuthorizedClientManager;
import org.springframework.security.oauth2.client.OAuth2AuthorizeRequest;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService;
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.OAuth2AccessToken;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.http.HttpHeaders;

import feign.RequestInterceptor;
import feign.RequestTemplate;

{{/hasOAuthMethods}}
{{/hasAuthMethods}}
import org.springframework.context.annotation.Configuration;
import java.util.Locale;

public class ClientConfiguration {

{{#authMethods}}
    {{#isBasicBasic}}
  @Value("${{openbrace}}{{#lambda.lowercase}}{{{title}}}{{/lambda.lowercase}}.security.{{{name}}}.username:{{closebrace}}")
  private String {{{name}}}Username;

  @Value("${{openbrace}}{{#lambda.lowercase}}{{{title}}}{{/lambda.lowercase}}.security.{{{name}}}.password:{{closebrace}}")
  private String {{{name}}}Password;

  @Bean
  @ConditionalOnProperty(name = "{{#lambda.lowercase}}{{{title}}}{{/lambda.lowercase}}.security.{{{name}}}.username")
  public BasicAuthRequestInterceptor {{{name}}}RequestInterceptor() {
    return new BasicAuthRequestInterceptor(this.{{{name}}}Username, this.{{{name}}}Password);
  }

    {{/isBasicBasic}}
    {{#isApiKey}}
  @Value("${{openbrace}}{{#lambda.lowercase}}{{{title}}}{{/lambda.lowercase}}.security.{{{name}}}.key:{{closebrace}}")
  private String {{{name}}}Key;

  @Bean
  @ConditionalOnProperty(name = "{{#lambda.lowercase}}{{{title}}}{{/lambda.lowercase}}.security.{{{name}}}.key")
  public ApiKeyRequestInterceptor {{{name}}}RequestInterceptor() {
    return new ApiKeyRequestInterceptor({{#isKeyInHeader}}"header"{{/isKeyInHeader}}{{^isKeyInHeader}}"query"{{/isKeyInHeader}}, "{{{keyParamName}}}", this.{{{name}}}Key);
  }

    {{/isApiKey}}
    {{#isOAuth}}
  private static final String CLIENT_PRINCIPAL_{{#lambda.uppercase}}{{{flow}}}{{/lambda.uppercase}} = "oauth2FeignClient";

  @Bean
  @ConditionalOnProperty( prefix = "spring.security.oauth2.client.registration.{{{name}}}{{#lambda.pascalcase}}{{{flow}}}{{/lambda.pascalcase}}", name = "enabled", havingValue = "true" )
  public OAuth2RequestInterceptor {{{flow}}}OAuth2RequestInterceptor(final OAuth2AuthorizedClientManager {{{flow}}}AuthorizedClientManager ) {
     return new OAuth2RequestInterceptor(OAuth2AuthorizeRequest.withClientRegistrationId("{{{name}}}{{#lambda.pascalcase}}{{{flow}}}{{/lambda.pascalcase}}")
            .principal( new AnonymousAuthenticationToken( CLIENT_PRINCIPAL_{{#lambda.uppercase}}{{{flow}}}{{/lambda.uppercase}}, CLIENT_PRINCIPAL_{{#lambda.uppercase}}{{{flow}}}{{/lambda.uppercase}}, AuthorityUtils.createAuthorityList( "ROLE_ANONYMOUS" ) ) )
            .build(), {{{flow}}}AuthorizedClientManager );
  }

  @Bean
  @ConditionalOnProperty( prefix = "spring.security.oauth2.client.registration.{{{name}}}{{#lambda.pascalcase}}{{{flow}}}{{/lambda.pascalcase}}", name = "enabled", havingValue = "true" )
  public OAuth2AuthorizedClientManager {{{flow}}}AuthorizedClientManager(ClientRegistrationRepository clientRegistrationRepository,
        OAuth2AuthorizedClientService authorizedClientService ) {
    return new AuthorizedClientServiceOAuth2AuthorizedClientManager( clientRegistrationRepository, authorizedClientService );
  }
    {{/isOAuth}}
{{/authMethods}}
{{#hasOAuthMethods}}
  public static class OAuth2RequestInterceptor implements RequestInterceptor {

    private final OAuth2AuthorizedClientManager oAuth2AuthorizedClientManager;
    private final OAuth2AuthorizeRequest oAuth2AuthorizeRequest;

    public OAuth2RequestInterceptor(OAuth2AuthorizeRequest oAuth2AuthorizeRequest,OAuth2AuthorizedClientManager oAuth2AuthorizedClientManager){
      this.oAuth2AuthorizeRequest = oAuth2AuthorizeRequest;
      this.oAuth2AuthorizedClientManager = oAuth2AuthorizedClientManager;
    }

    @Override
    public void apply( final RequestTemplate template ) {
      template.header( HttpHeaders.AUTHORIZATION, getBearerToken() );
    }

    public OAuth2AccessToken getAccessToken() {
      final OAuth2AuthorizedClient authorizedClient = oAuth2AuthorizedClientManager.authorize(oAuth2AuthorizeRequest);
      if (authorizedClient == null) {
        throw new OAuth2AuthenticationException( "Client failed to authenticate");
      }
     return authorizedClient.getAccessToken();
    }

    public String getBearerToken() {
      final OAuth2AccessToken accessToken = getAccessToken();
      return String.format( Locale.ROOT, "%s %s", accessToken.getTokenType().getValue(), accessToken.getTokenValue() );
    }
  }
{{/hasOAuthMethods}}

}
